package com.gking.resourcePool.AOP;

import com.gking.resourcePool.AOP.annotationUtil.validataParam.ValidateParam;
import com.gking.resourcePool.AOP.annotationUtil.validataParam.ValidateType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.List;
import java.util.Map;

@Slf4j
@Aspect
@Component
public class ValidateParamAspect {


    @Before("@annotation(validateParam)")
    public void validateParams(JoinPoint joinPoint, ValidateParam validateParam) throws IllegalArgumentException {

        // joinPoint.getArgs() 返回的是当前被切入方法的 实参数组，即当方法被调用时传递给它的实际参数的值。
        // 这些值与方法签名中的参数顺序一一对应。每个元素对应方法参数的实际值。
        // 如果方法有多个参数，那么数组长度等于方法的参数个数。如果某个参数为 null，对应位置的值也会是 null。
        Object[] args = joinPoint.getArgs();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // method.getParameters() 返回的是 方法的形参（参数声明）数组，即方法签名中定义的所有参数。
        // 它是 java.lang.reflect.Parameter 类型的数组，每个 Parameter 对象代表一个方法形参的详细信息。
        Parameter[] parameters = method.getParameters();

        // log.info("Arguments (args): {}", Arrays.deepToString(args));
        // log.info("Parameters: {}", Arrays.toString(parameters));

        ValidateParam.Field[] fields = validateParam.value();

        for (ValidateParam.Field fieldConfig : fields) {

            String fieldName = fieldConfig.field();
            ValidateType type = fieldConfig.type();
            boolean isNullAllowed = fieldConfig.isNull();
            Class<?> expectedType = fieldConfig.classType();
            String message = fieldConfig.message();

            // 根据指定来源获取参数值
            Object paramValue = findParamValue(fieldName, type, parameters, args, message);

            // 校验非空
            if (!isNullAllowed && ObjectUtils.isEmpty(paramValue)) {
                throw new IllegalArgumentException(message + ": Field '" + fieldName + "' cannot be null or empty.");
            }

            // 校验类型
            if (paramValue != null && !expectedType.isAssignableFrom(paramValue.getClass())) {
                throw new IllegalArgumentException(message + ": Field '" + fieldName + "' must be of type " + expectedType.getSimpleName() + ".");
            }
        }
    }


    private Object findParamValue(String fieldName, ValidateType type, Parameter[] parameters, Object[] args, String message) {
        switch (type) {
            case TYPE_RequestParam:
                return findRequestParamValue(fieldName, parameters, args, message);
            case TYPE_PathVariable:
                return findPathVariableValue(fieldName, parameters, args, message);
            case TYPE_RequestBody:
                return findRequestBodyValue(fieldName, parameters, args, message);
            default:
                throw new IllegalArgumentException("Unsupported validation type: " + type);
        }
    }


    private Object findRequestParamValue(String fieldName, Parameter[] parameters, Object[] args, String message) {
        for (int i = 0; i < parameters.length; i++) {
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if (requestParam != null) {
                // 优先按 @RequestParam 的 value 属性匹配，若未指定则按参数名匹配
                String paramName = requestParam.value().isEmpty() ? parameters[i].getName() : requestParam.value();
                if (fieldName.equals(paramName)) {
                    return args[i];
                }
            }
        }
        throw new IllegalArgumentException(message + ": RequestParam '" + fieldName + "' not found in method arguments.");
    }


    private Object findPathVariableValue(String fieldName, Parameter[] parameters, Object[] args, String message) {
        for (int i = 0; i < parameters.length; i++) {
            PathVariable pathVariable = parameters[i].getAnnotation(PathVariable.class);
            if (pathVariable != null) {
                // 优先按 @PathVariable 的 value 属性匹配，若未指定则按参数名匹配
                String paramName = pathVariable.value().isEmpty() ? parameters[i].getName() : pathVariable.value();
                if (fieldName.equals(paramName)) {
                    return args[i];
                }
            }
        }
        throw new IllegalArgumentException(message + ": PathVariable '" + fieldName + "' not found in method arguments.");
    }


    private Object findRequestBodyValue(String fieldName, Parameter[] parameters, Object[] args, String message) {
        for (int i = 0; i < parameters.length; i++) {
            boolean annotationPresent = parameters[i].isAnnotationPresent(RequestBody.class);
            if (annotationPresent) {
                Object body = args[i];
                if (body != null) {
                    return extractFieldFromBody(body, fieldName);
                }
            }
        }
        throw new IllegalArgumentException(message + ": Field '" + fieldName + "' not found in @RequestBody object.");
    }


    private Object extractFieldFromBody(Object body, String fieldName) {
        try {
            if (body instanceof Map) {
                // 如果是 Map，则直接从 Map 中获取字段
                return ((Map<?, ?>) body).get(fieldName);
            } else if (body instanceof List) {
                // 如果是 List，只判断是否为空
                List<?> list = (List<?>) body;
                if (list.isEmpty()) {
                    throw new IllegalArgumentException("List is empty in the @RequestBody List.");
                }
                // 只对list是否为空进行校验，不对list的项进行校验
                return list;
            } else {
                // 如果是 Bean，则通过反射获取字段
                return extractFieldFromBean(body, fieldName);
            }
        } catch (Exception e) {
            throw new IllegalArgumentException(
                    "Failed to extract field '" + fieldName + "' from @RequestBody object: " + e.getMessage(), e);
        }
    }


    /**
     * 从给定的对象（Bean）中提取指定字段的值。
     *
     * @param body      目标对象，该对象为通过 @RequestBody 注解接收到的实体类对象。
     * @param fieldName 需要提取的字段名称。
     * @return 提取到的字段值。如果字段不存在或无法访问，会抛出异常。
     * @throws IllegalArgumentException 如果目标对象中找不到指定字段，或者无法访问该字段值。
     *                                  <p>
     *                                  功能描述：
     *                                  1. 使用反射机制，尝试从对象的类中获取指定字段。
     *                                  2. 设置字段的访问权限为可访问（即使字段是 private）。
     *                                  3. 返回字段的值。
     *                                  4. 如果字段不存在或访问失败，抛出自定义的 IllegalArgumentException 异常，包含详细错误信息。
     *                                  <p>
     *                                  注意事项：
     *                                  - 确保目标对象是一个 Bean 或包含字段定义的类。
     *                                  - 仅适用于目标字段在类中直接定义的情况，不包括父类中的字段。
     *                                  - 如果字段名拼写错误或字段不存在，会抛出异常。
     *                                  - 对于复杂对象的处理（如嵌套对象），需要额外扩展逻辑。
     */
    private Object extractFieldFromBean(Object body, String fieldName) {
        try {
            Field field = body.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(body);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new IllegalArgumentException("Failed to extract field '" + fieldName + "' from @RequestBody object: " + e.getMessage(), e);
        }
    }
}